suite("view", function () {
    "use strict";

    var view = require("./view.jsx");
    var assert = require("assert");
    var React = require("react");
    require("react/addons");

    var root;

    setup(function () {
        var roots = document.getElementsByClassName("root");
        if (roots.length) {
            roots[0].parentNode.removeChild(roots[0]);
        }

        root = document.createElement("div");
        root.innerHTML = "<div class=\"root\"></div>";
        root.style.height = "300px";
        root.style.position = "relative";
        document.body.appendChild(root);
    });

    test("property renders", function () {
        React.render(<view.Property />, root);
    });

    test("selector renders with default properties", function () {
        React.render(<view.Selector />, root);
    });

    test("selector displays items", function (done) {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }]
        }, function () {
            assert(root.getElementsByClassName("layout").length === 3);
            done();
        });
    });

    test("selector selects item", function (done) {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],
            selectedIndex: 1
        }, function () {
            assert(root.getElementsByClassName("layout").length === 3);
            assert(root.getElementsByClassName("layout selected").length === 1);
            done();
        });
    });

    test("selector unselects item", function (done) {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],
            selectedIndex: 1
        }, function () {
            selector.setProps({selectedIndex: -1}, function () {
                assert(root.getElementsByClassName("layout").length === 3);
                assert(root.getElementsByClassName("layout selected").length === 0);
                done();
            });
        });
    });

    test("selector triggers select on item click", function (done) {
        var selector = React.render(<view.Selector />, root);
        var clickedIndex = -1;
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],
            onSelect: function (e) {
                clickedIndex = e.index;
            }
        }, function () {
            var itemNodes = root.getElementsByClassName("layout");
            React.addons.TestUtils.Simulate.click(itemNodes[1]);
            setTimeout(function () {
                assert(clickedIndex === 1);
                done();
            });
        });
    });

    test("selector requests new", function () {
        var selector = React.render(<view.Selector />, root);
        var requested = false;
        selector.setProps({
            onRequestNew: function () {
                requested = true;
            }
        });
        var button = root.getElementsByClassName("request-new")[0];
        React.addons.TestUtils.Simulate.click(button);
        assert(requested);
    });

    test("layout renders with default props", function () {
        React.render(<view.Layout />, root);
    });

    test("layout is initially hidden", function () {
        React.render(<view.Layout />, root);
        var els = root.getElementsByClassName("layout-editor hidden");
        assert(els.length === 1);
    });

    test("layout triggers delete on delete click", function (done) {
        var layout = React.render(<view.Layout />, root);
        var deleteRequested = false;
        layout.setProps({
            onDelete: function () {
                deleteRequested = true;
            }
        });
        var button = root.getElementsByClassName("delete-layout")[0];
        React.addons.TestUtils.Simulate.click(button);
        setTimeout(function () {
            assert(deleteRequested);
            done();
        });
    });

    test("document renders with default props", function () {
        React.render(<view.Document />, root);
    });

    test("document propagates properties to children", function (done) {
        var document = React.render(<view.Document />, root);
        document.setProps({
            "template-selector": {
                items: [{
                    id: 0
                }, {
                    id: 1
                }, {
                    id: 2
                }],
                selectedIndex: 1
            },
            "page-selector": {
                items: [{
                    id: 3
                }, {
                    id: 4
                }, {
                    id: 5
                }],
                selectedIndex: 2
            },
            "layout-editor": {
                visible: true
            }
        }, function () {
            var templatesRoot = document.refs["template-selector"].getDOMNode();
            var layouts = templatesRoot.getElementsByClassName("layout");
            assert(layouts.length === 3);
            assert(layouts[1].className.indexOf("selected") >= 0);

            var pagesRoot = document.refs["page-selector"].getDOMNode();
            layouts = pagesRoot.getElementsByClassName("layout");
            assert(layouts.length === 3);
            assert(layouts[2].className.indexOf("selected") >= 0);

            var layoutRoot = document.refs["layout-editor"].getDOMNode();
            assert(layoutRoot.className.indexOf("hidden") < 0);

            done();
        });
    });

    test("selector registers drag start", function () {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }]
        });
        var layouts = root.getElementsByClassName("layout");
        var e = {
            target: {
                getBoundingClientRect: function () {
                    return {top: 120};
                }
            },
            clientY: 450,
            dataTransfer: {
                setData: function (type, data) {
                    this.type = type;
                    this.data = data;
                }
            }
        };
        React.addons.TestUtils.Simulate.dragStart(layouts[1], e);
        assert(e.dataTransfer.type === "text/plain");
        assert(e.dataTransfer.data === "layout:1:330");
    });

    test("selector handles only own drop", function () {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],

            onPosition: function () {
                assert(false);
            }
        });

        React.addons.TestUtils.Simulate.drop(selector.getDOMNode(), {
            dataTransfer: {
                getData: function () {
                    return "";
                }
            }
        });

        React.addons.TestUtils.Simulate.drop(selector.getDOMNode(), {
            dataTransfer: {
                getData: function () {
                    return "some data";
                }
            }
        });
    });

    test("selector triggers position on drag drop, only if moved", function () {
        var selector = React.render(<view.Selector />, root);
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],

            onPosition: function () {
                assert(false);
            }
        });

        var dom = selector.getDOMNode();
        var dragStartOffset = 15;
        var layouts = dom.getElementsByClassName("layout");
        var firstRect = layouts[0].getBoundingClientRect();
        var secondRect = layouts[1].getBoundingClientRect();
        var thirdRect = layouts[2].getBoundingClientRect();

        React.addons.TestUtils.Simulate.drop(dom, {
            dataTransfer: {
                getData: function () {
                    return "layout:1:" + dragStartOffset;
                }
            },

            clientY: (firstRect.top + secondRect.top) / 2 + dragStartOffset
        });

        React.addons.TestUtils.Simulate.drop(dom, {
            dataTransfer: {
                getData: function () {
                    return "layout:1:" + dragStartOffset;
                }
            },

            clientY: (secondRect.top + thirdRect.top) / 2 + dragStartOffset
        });
    });

    test("selector triggers position", function () {
        var selector = React.render(<view.Selector />, root);
        var called = false;
        var expectedArgs = null;
        selector.setProps({
            items: [{
                id: 0
            }, {
                id: 1
            }, {
                id: 2
            }],

            onPosition: function (e) {
                called = true;
                assert(e.newIndex === expectedArgs.newIndex);
                assert(e.oldIndex === expectedArgs.oldIndex);
            }
        });

        var dom = selector.getDOMNode();
        var dragStartOffset = 15;
        var layouts = dom.getElementsByClassName("layout");
        var firstRect = layouts[0].getBoundingClientRect();
        var secondRect = layouts[1].getBoundingClientRect();
        var thirdRect = layouts[2].getBoundingClientRect();

        expectedArgs = {
            oldIndex: 1,
            newIndex: 0
        };
        React.addons.TestUtils.Simulate.drop(dom, {
            dataTransfer: {
                getData: function () {
                    return "layout:1:" + dragStartOffset;
                }
            },

            clientY: firstRect.top / 2 + dragStartOffset
        });
        assert(called, "called");
        called = false;

        expectedArgs = {
            oldIndex: 1,
            newIndex: 2
        };
        React.addons.TestUtils.Simulate.drop(dom, {
            dataTransfer: {
                getData: function () {
                    return "layout:1:" + dragStartOffset;
                }
            },

            clientY: thirdRect.top * 2 + dragStartOffset
        });
    });

    test("block asset handles drag start", function () {
        var blockAsset = React.render(<view.BlockAsset type="test" />, root);
        var e = {
            dataTransfer: {
                setData: function (type, data) {
                    this.type = type;
                    this.data = data;
                }
            }
        };

        React.addons.TestUtils.Simulate.dragStart(blockAsset.getDOMNode(), e);
        assert(e.dataTransfer.type === "text/plain");
        assert(e.dataTransfer.data === "block-asset:test");
    });

    test("layout editor triggers new block, only when block asset is dropped", function () {
        var layout = React.render(<view.Layout />, root);
        layout.setProps({
            onNewBlock: function () {
                assert(false);
            }
        });

        var e = {
            dataTransfer: {
                getData: function () {
                    return "";
                }
            }
        };
        React.addons.TestUtils.Simulate.drop(layout.getDOMNode(), e);

        e.dataTransfer.getData = function () {
            return "some data";
        };
        React.addons.TestUtils.Simulate.drop(layout.getDOMNode(), e);
    });

    test("layout editor triggers new block on block asset drop", function () {
        var layout = React.render(<view.Layout />, root);
        var called = false;
        layout.setProps({
            onNewBlock: function (e) {
                called = true;
                assert(e.type === "text");
            }
        });

        var e = {
            dataTransfer: {
                getData: function () {
                    return "block-asset:text";
                }
            }
        };
        React.addons.TestUtils.Simulate.drop(layout.getDOMNode(), e);
        assert(called);
    });

    test("selector layout provides select event", function () {
        var layout = React.render(<view.SelectorLayout />, root);
        var called = false;
        layout.setProps({
            onSelect: function () {
                called = true;
            }
        });
        React.addons.TestUtils.Simulate.click(layout.getDOMNode());
        assert(called);
    });

    test("selector layout provides drag start event", function () {
        var layout = React.render(<view.SelectorLayout />, root);
        var called = false;
        layout.setProps({
            onDragStart: function () {
                called = true;
            }
        });
        React.addons.TestUtils.Simulate.dragStart(layout.getDOMNode());
        assert(called);
    });

    test("selector layout displays blocks", function () {
        var layout = React.render(<view.SelectorLayout />, root);
        layout.setProps({
            blocks: [{
                id: 0,
                text: "block 0",
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.5,
                    height: 0.5
                },
                zIndex: 1
            }, {
                id: 1,
                text: "block 1",
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.5,
                    height: 0.5
                },
                zIndex: 1
            }, {
                id: 2,
                text: "block 2",
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.5,
                    height: 0.5
                },
                zIndex: 1
            }]
        });
        React.addons.TestUtils.Simulate.dragStart(layout.getDOMNode());
        var blocks = layout.getDOMNode().getElementsByClassName("block");
        assert(blocks.length === 3);
        assert(blocks[0].innerHTML.trim() === "block 0");
        assert(blocks[0].style.left === "30%");
        assert(blocks[0].style.top === "30%");
        assert(blocks[0].style.width === view.adjustedSelectorBlockSize(0.5));
        assert(blocks[0].style.height === view.adjustedSelectorBlockSize(0.5));
        assert(blocks[1].innerHTML.trim() === "block 1");
        assert(blocks[2].innerHTML.trim() === "block 2");
    });

    test("editor block moves on mouse drag, only on left button", function () {
        var block = React.render(<view.EditorBlock />, root);
        block.setProps({
            block: {
                id: 1,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 1
            },

            onMove: function () {
                assert(false);
            }
        });

        React.addons.TestUtils.Simulate.mouseDown(block.getDOMNode(), {
            button: 1,
            clientX: 3,
            clientY: 4
        });

        view.windowEvents.trigger("mousemove", {
            clientX: 4,
            clientY: 5
        });
    });

    test("editor block moves on mouse drag", function () {
        var block = React.render(<view.EditorBlock />, root);
        var called = 0;
        block.setProps({
            block: {
                id: 1,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 1
            },

            onMove: function (e) {
                called++;
                switch (called) {
                case 1:
                    assert(e.block.id === 1);
                    assert(e.node === block.getDOMNode());
                    assert(e.moving);
                    break;
                case 2:
                    assert(e.block.id === 1);
                    assert(e.node === block.getDOMNode());
                    assert(!e.moving);
                    break;
                }
            }
        });

        var blockRect = block.getDOMNode().getBoundingClientRect();
        React.addons.TestUtils.Simulate.mouseDown(block.getDOMNode(), {
            button: 0,
            clientX: blockRect.left + 30,
            clientY: blockRect.top + 45
        });

        view.windowEvents.trigger("mousemove", {
            clientX: blockRect.left + 33,
            clientY: blockRect.top + 48
        });

        view.windowEvents.trigger("mouseup", {
            clientX: blockRect.left + 36,
            clientY: blockRect.top + 51
        });

        assert(called === 2);
    });

    test("layout editor handles block move", function () {
        var layout = React.render(<view.Layout />, root);
        var called = 0;
        var blockRect;
        var editorRect;
        layout.setProps({
            visible: true,
            blocks: [{
                id: 1,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 1
            }],

            onBlockMove: function (e) {
                called++;
                switch (called) {
                case 1:
                    assert(e.blockId === 1);
                    assert(e.moving);
                    break;
                case 2:
                    assert(e.blockId === 1);
                    assert(!e.moving);
                    break;
                }
            }
        });

        var node = layout.getDOMNode();
        var block = node.getElementsByClassName("block")[0];
        block.parentNode.style.height = "300px";
        blockRect = block.getBoundingClientRect();
        editorRect = node.getBoundingClientRect();

        React.addons.TestUtils.Simulate.mouseDown(block, {
            button: 0,
            clientX: blockRect.left + 30,
            clientY: blockRect.top + 45
        });

        view.windowEvents.trigger("mousemove", {
            clientX: blockRect.left + 33,
            clientY: blockRect.top + 48
        });

        view.windowEvents.trigger("mouseup", {
            clientX: blockRect.left + 36,
            clientY: blockRect.top + 51
        });

        assert(called === 2);
    });

    test("layout editor displays blocks", function () {
        var layout = React.render(<view.Layout />, root);
        layout.setProps({
            visible: true,
            blocks: [{
                id: 0,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 0
            }, {
                id: 1,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 1
            }, {
                id: 2,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 2
            }]
        });
        var blocks = layout.getDOMNode().getElementsByClassName("block");
        assert(blocks.length === 3);
        assert(blocks[0].style.left === "30%");
        assert(blocks[0].style.top === "30%");
        assert(blocks[0].style.width === "45%");
        assert(blocks[0].style.height === "45%");
    });

    test("editor block detects border click as resize", function () {
        var block = React.render(<view.EditorBlock />, root);
        var called = 0;
        block.setProps({
            block: {
                id: 1,
                position: {
                    x: 0.3,
                    y: 0.3
                },
                size: {
                    width: 0.45,
                    height: 0.45
                },
                zIndex: 1
            },

            onResize: function (e) {
                called++;
                assert(e.mode === 0);
            }
        });

        var blockRect = block.getDOMNode().getBoundingClientRect();
        React.addons.TestUtils.Simulate.mouseDown(block.getDOMNode(), {
            button: 0,
            clientX: blockRect.left + 9,
            clientY: blockRect.top + 9
        });

        view.windowEvents.trigger("mousemove", {
            clientX: blockRect.left + 33,
            clientY: blockRect.top + 48
        });

        assert(called === 1);
    });

    test("layout editor resizes block on resize event", function () {
        var layout = React.render(<view.Layout />, root);
        var block = {
            id: 1,
            position: {
                x: 0.3,
                y: 0.3
            },
            size: {
                width: 0.45,
                height: 0.45
            },
            zIndex: 1
        };
        layout.setProps({
            visible: true,
            blocks: [block]
        });

        var blockNode = layout.getDOMNode().getElementsByClassName("block")[0];
        var blockRect = blockNode.getBoundingClientRect();
        layout.handleBlockResize({
            mode: 0,
            clientX: blockRect.left - 9,
            clientY: blockRect.top - 9,
            node: blockNode,
            block: block
        });

        assert(parseInt(blockNode.style.left.substr(0, blockNode.style.left.length - 1), 10) < 30);
        assert(parseInt(blockNode.style.top.substr(0, blockNode.style.top.length - 1), 10) < 30);
        assert(parseInt(blockNode.style.width.substr(0, blockNode.style.width.length - 1), 10) > 45);
        assert(parseInt(blockNode.style.height.substr(0, blockNode.style.height.length - 1), 10) > 45);
    });

    test("layout editor triggers resize on block resize", function () {
        var layout = React.render(<view.Layout />, root);
        var block = {
            id: 1,
            position: {
                x: 0.3,
                y: 0.3
            },
            size: {
                width: 0.45,
                height: 0.45
            },
            zIndex: 1
        };
        var called = false;
        layout.setProps({
            visible: true,
            blocks: [block],

            onBlockResize: function () {
                called = true;
            }
        });

        var blockNode = layout.getDOMNode().getElementsByClassName("block")[0];
        var blockRect = blockNode.getBoundingClientRect();
        layout.handleBlockResize({
            mode: 0,
            clientX: blockRect.left - 9,
            clientY: blockRect.top - 9,
            node: blockNode,
            block: block
        });

        assert(called);
    });
});
